import {
    emptyNode,
    HTML_NODE_TYPE,
    HTML_TAG_STATUS
} from "../../constant";

import HNode from "../../type/HNode";

import {ErrorMessage} from "./ErrorMessage";
import {errorProcess} from "./helper";

import {
    parseNode,
    parseScriptOrStyle,
    parseTextarea
} from "./tagParser";

const PLAIN_TEXT_TAG_REGEX = /script|textarea|style/;

export class HtmlParser {

    // static HTML_STR = undefined;
    // static PLAIN_TEXT_TAG = undefined;
    // 记录的是遍历的字符数  （例如 \t 在视觉上有4个字符宽度，但在这里记作 1）
    // static charCount = 0;

    constructor() {
        this.HTML_STR = undefined;
        this.PLAIN_TEXT_TAG = undefined;
        this.charCount = 0;
    }

    /**
     * 分析模板内容，根据模板内容解析出 HNode 节点
     * @param {String} htmlStr
     */
    parse(htmlStr) {
        this.HTML_STR = htmlStr;
        // 初始化工作
        const elementStack = new Array();
        this.initElementStack(elementStack);
        // 分析字符串
        while (this.HTML_STR.length !== 0) {
            const Result = parseNode(this);
            const hNode = Result.HTML_NODE;
            // 空文本（或其他特殊情况）
            if (hNode === emptyNode) {
                continue;
            }
            // 处理特殊标签 script style textarea
            if (this.checkIsPlainTextTag(hNode, this)) {
                this.parsePlainTextTag(hNode, this);
                // 将特殊节点装入 elementStack
                const parent = elementStack.pop();
                hNode.parent = parent;
                parent.children.push(hNode);
                elementStack.push(parent);
                continue;
            }
            // 处理其他一般标签
            switch (hNode.status) {
                // 开口标签处理
                case (HTML_TAG_STATUS.OPEN): {
                    elementStack.push(hNode);
                    break;
                }
                // 闭口标签处理
                case (HTML_TAG_STATUS.CLOSE): {
                    const child = elementStack.pop();
                    if (child.tag === hNode.tag) {
                        const parent = elementStack.pop();
                        child.parent = parent;
                        child.status = HTML_TAG_STATUS.SINGLE;
                        parent.children.push(child);
                        elementStack.push(parent);
                    } else {
                        // 语法错误报错
                        errorProcess(hNode, ErrorMessage.NoMatchTag, this.HTML_STR);
                    }
                    break;
                }
                // 独立标签处理
                case (HTML_TAG_STATUS.SINGLE): {
                    const parent = elementStack.pop();
                    hNode.parent = parent;
                    parent.children.push(hNode);
                    elementStack.push(parent);
                    break;
                }
            }
        }
        // 返回根节点
        if (elementStack.length !== 1) {
            // 语法错误报错
            errorProcess(hNode, ErrorMessage.HasMultiRootNode, this.HTML_STR);
        }
        // 去除 initElementStack 推入的根包装节点
        return elementStack[0].children[0];
    }

    /**
     * 初始化html节点栈，
     * 这里装入了一个假的根节点，用于包容传入多个根节点的情况
     * @param {Array<HNode>} elementStack
     */
    initElementStack(elementStack) {
        const rootHNode = new HNode(
            'root',
            HTML_NODE_TYPE.TAG,
            HTML_TAG_STATUS.SINGLE,
            undefined
        );
        elementStack.push(rootHNode);
    }

    /**
     * 检查标签是否是一个 特殊的文本标签，比如script，textarea，style
     * 这类标签的内容不需要经过处理
     * @param {HNode} hNode
     * @param {HtmlParser} parser
     */
    checkIsPlainTextTag(hNode, parser) {
        const tagName = hNode.tag;
        if (PLAIN_TEXT_TAG_REGEX.test(tagName)) {
            const Result = tagName.match(PLAIN_TEXT_TAG_REGEX);
            parser.PLAIN_TEXT_TAG = Result[0];
            return true;
        }
        return false;
    }

    /**
     * 处理 script，textarea，style 这类特殊的文本标签
     * @param {HNode} hNode
     * @param {HtmlParser} parser
     */
    parsePlainTextTag(hNode, parser) {
        // 检查是否需要进行 特殊文本解析
        if (hNode.status === HTML_TAG_STATUS.SINGLE) {
            return;
        }
        if (hNode.status === HTML_TAG_STATUS.CLOSE) {
            errorProcess(hNode, ErrorMessage.NoMatchTag, parser.HTML_STR);
        }
        // 进行文本解析
        const tagName = parser.PLAIN_TEXT_TAG;
        switch (tagName) {
            case ("script"):
            case ("style"): {
                parseScriptOrStyle(hNode, parser);
                break;
            }
            case ("textarea"): {
                parseTextarea(hNode, parser);
                break;
            }
        }
    }

}
